
/*
OV7670 Camera Module / SCCB (Serial Camera Control Bus) Interface
Copyright (C) 2012 Craig Dawson, Aaron Mohtar, Craig Peacock

This example uses the OV7670 AL422 FIFO Camera Module (V1) from emartee.com

    Pin 1   3.3V     Connect to 3V3
    Pin 2   GND      Connect to GND
    Pin 3   SCCB_C   Connect to RA2  (Requires 4k7 pull-up)
    Pin 4   SCCB_D   Connect to RA3  (Requires 4k7 pull-up)
    Pin 5   VSYNC    Connect to WRST on OV7670
    Pin 6   WRST     Connect to VSYNC
    Pin 7   WEN      Connect to 3V3
    Pin 8   XCLK     Not connected
    Pin 9   RRST     Connect to RB12
    Pin 10  OE       Connect to RB10
    Pin 11  RCLK     Connect to RB11
    Pin 12  GND      Connect to GND
    Pin 13  D0       Connect to RB0
    Pin 14  D1       Connect to RB1
    Pin 15  D2       Connect to RB2
    Pin 16  D3       Connect to RB3
    Pin 17  D4       Connect to RB4
    Pin 18  D5       Connect to RB5
    Pin 19  D6       Connect to RB6
    Pin 20  D7       Connect to RB7

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <p32xxxx.h>
#include "SSD1289.h"
#include "OV7670.h"

void ov7670Init(void)
{
    unsigned char reg_hstart;
    unsigned char reg_hstop;
    unsigned char reg_href;

    unsigned char reg_vstart;
    unsigned char reg_vstop;
    unsigned char reg_vref;

    unsigned int hstart;
    unsigned int hstop;

    unsigned int vstart;
    unsigned int vstop;

    AD1PCFG = 0xFFFF;

    OV7670_FIFO_RRST_TRIS = 0;
    OV7670_FIFO_OEN_TRIS  = 0;
    OV7670_FIFO_RCLK_TRIS = 0;

    OV7670_FIFO_RCLK = 0;
    OV7670_FIFO_OEN = 0;
    OV7670_FIFO_RRST = 0;

    // Initialise Serial Camera Control Bus
    sccbInit();

    // Read product ID
    printf("Product ID (MSB) = 0x%02X\r\n",sccbReadCommand(OV7670_I2C_ADDR, OV7670_PID));    
    printf("Product ID (LSB) = 0x%02X\r\n",sccbReadCommand(OV7670_I2C_ADDR, OV7670_VER));    

    // Reset image sensor
    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_COM7, 0b10000000);
    delayMs(100);

    // Set VSYNC negative - YSYNC is connected to WRST
    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_COM10, 0x02);
 
    // Do not let sensor automatically set window after resolution change.
    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_TSLB, 0b00000000);

    // Place Image Sensor in QVGA Mode, RGB Mode
    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_COM7, 0b00010100);

    // Enable RGB444 mode, only effective when COM15[4] is high
    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_RGB444, 0b00000011);

    // Enable RGB565, only effective when RGB444[1] above is low
    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_COM15, 0b00010000);

    // Turn on Test Pattern - 8-bar color bar
    //sccbWriteCommand(OV7670_I2C_ADDR, OV7670_SCALING_XSC, 0b00000000);
    //sccbWriteCommand(OV7670_I2C_ADDR, OV7670_SCALING_YSC, 0b10000000);

    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_HSTART, 0x17);
    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_HSTOP,  0x05);
    sccbWriteCommand(OV7670_I2C_ADDR, OV7670_HREF,   0x80);

    //sccbWriteCommand(OV7670_I2C_ADDR, OV7670_VSTART, 0x02);
    //sccbWriteCommand(OV7670_I2C_ADDR, OV7670_VSTOP,  0x03);
    //sccbWriteCommand(OV7670_I2C_ADDR, OV7670_VREF,   0x0A);

    // Run some diagnostics/calcs on the window size
    reg_vref   = sccbReadCommand(OV7670_I2C_ADDR, OV7670_VREF);
    reg_href   = sccbReadCommand(OV7670_I2C_ADDR, OV7670_HREF);
    reg_hstart = sccbReadCommand(OV7670_I2C_ADDR, OV7670_HSTART);
    reg_hstop  = sccbReadCommand(OV7670_I2C_ADDR, OV7670_HSTOP);
    reg_vstart = sccbReadCommand(OV7670_I2C_ADDR, OV7670_VSTART);
    reg_vstop  = sccbReadCommand(OV7670_I2C_ADDR, OV7670_VSTOP);

    hstart = (reg_hstart << 3) | (reg_href & 0x07);
    hstop = (reg_hstop << 3) | ((reg_href & 0x38) >> 3);

    vstart = (reg_vstart << 2) | (reg_vref & 0x03);
    vstop = (reg_vstop << 2) | ((reg_vref & 0x0C) >> 2);

    printf("hstart = %d\r\n", hstart);
    printf("hstop  = %d\r\n", hstop);
    printf("hrows  = %d\r\n", hstop - hstart);
    
    printf("vstart = %d\r\n", vstart);
    printf("vstop  = %d\r\n", vstop);
    printf("vlines  = %d\r\n", vstop - vstart);
}

void ov7670DisplayFrame(void)
{
    unsigned int x;
    unsigned int y;

    unsigned char RGB565_High = 0;
    unsigned char RGB565_Low = 0;
    unsigned int  RGB565 = 0;

    // Reset read address counter to zero.
    OV7670_FIFO_RRST = 0;
    delayUs(10);
    OV7670_FIFO_RCLK = 0;
    delayUs(10);
    OV7670_FIFO_RCLK = 1;
    delayUs(10);
    OV7670_FIFO_RRST = 1;

    // Image data starts from top left corner
    for (y = 0; y < 240; y++)
    {
        for (x = 0; x < 320; x++)
        {
            OV7670_FIFO_RCLK = 1;
            RGB565_Low = (PORTB & 0xFF);
            OV7670_FIFO_RCLK = 0;
            OV7670_FIFO_RCLK = 1;
            RGB565_High = (PORTB & 0xFF);
            OV7670_FIFO_RCLK = 0;
            RGB565 = ((RGB565_High << 8) + RGB565_Low);
            lcdSetPixel(y, x, RGB565);
        }
    }
}

void sccbInit(void)
{
    // Initialise I2C2 (RA2/RA3) for 400KHz
    I2C2CONbits.ON = 0;     // Disable I2C
    IFS1bits.I2C2MIF = 0;   // Clear I2C2 Master Interrupt Flag
    IFS1bits.I2C2BIF = 0;   // Clear I2C2 Bus Colllision Flag
    //IEC1bits.I2C2MIE = 1; // Enable I2C2 Master Interrupt Event
    //IEC1bits.I2C2BIE = 1; // Enable I2C2 Bus Collision Event
    I2C2BRG = 89;           // 400KHz @ 80MHz
    I2C2CONbits.ON = 1;     // Enable I2C
}

unsigned char sccbWriteCommand(unsigned char address, unsigned char command, unsigned char data)
{
    unsigned char recv_byte;

    I2C2CONbits.SEN = 1;        // Initiate I2C start
    while (I2C2CONbits.SEN);    // Wait for start event to complete

    // Send I2C Address
    IFS1bits.I2C2MIF = 0;       // Clear I2C2 Master Interrupt Flag
    I2C2TRN = address;          // Load address byte
    // Wait for transaction to complete
    while (!IFS1bits.I2C2MIF) {
        if (I2C2STATbits.BCL) {
            // Bus collision has occured
            printf("Bus Collision\r\n");
        }
    };

    if(I2C2STATbits.ACKSTAT) {
        printf("Error: I2C address not acknowledged\r\n");
        /* Handle error here */
    }

    // Send Command Byte
    IFS1bits.I2C2MIF = 0;       // Clear I2C2 master interrupt flag
    I2C2TRN = command;          // Load command byte
    while (!IFS1bits.I2C2MIF);  // Wait for transaction to complete

    if(I2C2STATbits.ACKSTAT) {
        printf("Error: Command byte not acknowledged\r\n");
        /* Handle error here */
    }

    // Send Data Byte
    IFS1bits.I2C2MIF = 0;       // Clear I2C2 master interrupt flag
    I2C2TRN = data;             // Load data byte
    while (!IFS1bits.I2C2MIF);  // Wait for transaction to complete

    if(I2C2STATbits.ACKSTAT) {
        printf("Error: Data byte not acknowledged\r\n");
        /* Handle error here */
    }

    I2C2CONbits.PEN = 1;        // Send stop bit
    while (I2C2CONbits.PEN);    // Wait for stop event to complete
}

unsigned char sccbReadCommand(unsigned char address, unsigned char command)
{
    unsigned char recv_byte;

    I2C2CONbits.SEN = 1;        // Initiate I2C start
    while (I2C2CONbits.SEN);    // Wait for start event to complete

    // Send I2C Address
    IFS1bits.I2C2MIF = 0;       // Clear I2C2 master interrupt flag
    I2C2TRN = address;          // Load address byte
    // Wait for transaction to complete
    while (!IFS1bits.I2C2MIF) {
        if (I2C2STATbits.BCL) {
            // Bus collision has occured
            printf("Bus Collision\r\n");
        }
    };

    if(I2C2STATbits.ACKSTAT) {
        printf("Error: I2C address not acknowledged\r\n");
        /* Handle error here */
    }

    // Send Command Byte
    IFS1bits.I2C2MIF = 0;       // Clear I2C2 master interrupt flag
    I2C2TRN = command;          // Load command byte
    while (!IFS1bits.I2C2MIF);  // Wait for transaction to complete

    if(I2C2STATbits.ACKSTAT) {
        printf("Error: Command byte not acknowledged\r\n");
        /* Handle error here */
    }

    I2C2CONbits.PEN = 1;        // Send stop bit
    while (I2C2CONbits.PEN);    // Wait for stop event to complete

    // Start read cycle
    // *****************************

   I2C2CONbits.SEN = 1;         // Initiate I2C start
    while (I2C2CONbits.SEN);    // Wait for start event to complete

    // Send I2C Address
    address = address | 0x01;   // Set read flag
    IFS1bits.I2C2MIF = 0;       // Clear I2C2 master interrupt flag
    I2C2TRN = address;          // Load address byte
    // Wait for transaction to complete
    while (!IFS1bits.I2C2MIF) {
        if (I2C2STATbits.BCL) {
            // Bus collision has occured
            printf("Bus Collision\r\n");
        }
    };

    if(I2C2STATbits.ACKSTAT) {
        printf("Error: I2C address not acknowledged\r\n");
        /* Handle error here */
    }

    I2C2CONbits.RCEN = 1;       // Enable read
    while (I2C2CONbits.RCEN);   // Wait for operation to complete
    recv_byte = I2C2RCV;        // Retrieve byte

    // Generate ACK
    I2C2CONbits.ACKDT = 1;      // Send negative ack
    I2C2CONbits.ACKEN = 1;      // Start operation
    while (I2C2CONbits.ACKEN);  // Wait for operation to complete

    I2C2CONbits.PEN = 1;        // Send stop bit
    while (I2C2CONbits.PEN);    // Wait for stop event to complete

    return(recv_byte);
}

